#ifndef LINK_H
#define LINK_H
#include <QtDebug>

#include "diagramTextItem.h"
#include <QGraphicsPathItem>
#include <QStyleOptionGraphicsItem>
#include <QBrush>
#include <QPen>

#define STEP 10 //!< Радиус скругления

class box;
class diagram;

/*! \class segment
	\brief Класс сегмента стрелки.Именно в этом классе реализована функция отрисовки пути.
*/
class segment{				
public:
    enum Direction{Up=1,Down=2,Right=4,Left=8}; //!< Направление сегмента
    QPointF p1;									//!< Точка начала сегмента
    QPointF p2;									//!< Точка конца сегмента
    segment *prev;								//!< Предыдущий сегмент
    QList<segment*> next;						//!< Следующие сегменты

    /*! Конструктор с параметром
		\param pos Позиция сегмента
    */
    segment(QPointF pos){prev = NULL; p1=pos; p2 = pos + QPointF(STEP,0);}
	
	/*! Конструктор с параметром
		\param previos Указатель на сегмент
    */
	segment(segment *previous);

	/*! Конструктор по умолчанию
    */
    segment(){prev = NULL;}
	~segment();


    /*! Сборка пути стрелки из сегментов
		\return	Путь Стрелки
    */
	QPainterPath formPath();

    /*! Продолжение пути до заданной точки

        Если точка лежит на продолжении сегмента, то он будет продлен до неё, и будет считаться конечным.
        Иначе он будет продлен до точки поворота, и построен новый, от точки поворота до конечной точки.
        \param end	Конечная точка
        \return		Указатель на получившийся конечный сегмент
    */
	segment *extendTo(QPointF end);

    /*! Удаление ветви стрелки от конца до ближайшего места ветвления
        \param end	Конечный сегмент удаляемой ветви
    */
	static void deleteBranch(segment *end);

	static void swap(segment *_old, segment *_new);
	segment * extendFrom(QPointF);


	 /*! Возврат сегмента ближайшего к заданной точке
        \param pos	Координаты
		\param R Радиус
		\return Указатель на сегмент
    */
	segment *closestTo(QPointF pos,float *R = 0);
        
	/*! Определение направления сегмента
        \return	Направление сегмента
    */
    Direction direction();

    /*! Получение сдвига между фактическим и графическим концом сегмента

        Графический конец(ГК) - точка, где сегмент переходит в кривую поворота, находится на расстоянии STEP от ФК
        Фактический конец(ФК) - точка, где кончался бы сегмент, если бы не было сглаживания, совпадает с фактическим начлом следущего сегмента

        \return	Сдвиг
    */
	QPointF gap();

	/*! Проверка, является ли сегмент начальным
        \return	Результат проверки
    */
    inline bool isRoot() {return prev == NULL;}
    
	/*! Проверка, является ли сегмент конечным
        \return	Результат проверки
    */
	inline bool isLeaf() {return next.size() == 0;}
    
	/*! Возврат длины сегмента
        \return	Длина сегмента
    */
	inline qreal len(){return QLineF(p1,p2).length();}

	/*! Проверка возможности переместить сегмент в данную точку
        \return	Результат проверки
    */
	bool canMoveEndTo(QPointF pos);

	inline segment *getSegmentByPoint(QPointF end){
		segment *res = NULL;
		if (p2 == end)
			return this;
		else
			for (int i = 0; i < next.size() && res == NULL; i++)
				res = next[i]->getSegmentByPoint(end);
		return res;
	}
};

/*! \class arrow
    \brief Класс стрелки.
*/
class link : public QGraphicsPathItem{
public:
        enum { Type = UserType + 2 };										//!< Тип элемента
		enum ArrowType{Input, Control, Output, Resource, NoType};			//!< Тип дуги
		enum EndType{FROM_CONTEXT=1,TO_CONTEXT=2,FROM_TUNNEL=4,TO_TUNNEL=8};//!< Тип конца дуги	
		
		/*! Конструктор по умолчанию
		*/
		link();

		/*! Конструктор копирования
		*/
		link(const link&);

		~link();

		/*	\struct end
			\brief Структура конца стрелки
		*/
		struct end{
			box *b;				//!< Блок, инцидентныый этому концу
			ArrowType type;		//!< Тип ветви дуги, оканчивающейся этим концом
			diagram *d;			//!< Диаграмма, на которую переносится стрелка. Если такой нет - NULL
			link *next;			//!< Стрелка, являющаяся продолжением конца на диаграмме d. Если такой нет - NULL
			segment* s;			//!< Конечный сегмент конца
			short int flags;	//!< Флаги типа конца

			end(box *_b, ArrowType _type, segment *_s)		//!< Конструктор
			{b=_b;type=_type;s=_s;d=NULL;next=NULL;flags=0;}
			~end(){//delete s;
			}
			inline void setEndFlag(EndType flag, bool v = true) //!< Установка/снятие заданного флага конца стрелки
			{if(v) flags |= flag; else flags &= !flag;} 
			inline bool testEndFlag(int f){return flags&f;}		//!< Проверка состояния заданого флага конца стрелки
		};


        int id;									//!< Идентификатор стрелки
        QString name;                           //!< Название стрелки
		end *from;								//!< Начало
	    QMultiHash<int,end*> to; 				//!< Концы (ключ - идентификатор блока-приемника)
	
        QGraphicsPathItem *visPart;             //!< Графическое изображение стрелки
		diagramTextItem *annotation;			//!< Аннотация стрелки
	
		/*! Обновление формы стрелки
		*/
        inline void update(){setPath(from->s->formPath());	visPart->setPath(path());}
		
		/*! Возврат типа элемента
			\return Тип элемента
		*/
		int type() const{ return Type;}
		
		/*! Нахождение конца по его сегменту
			\param s Указатель на сегмент
			\return Указатель на конечный сегмент
		*/
		end *getEndBySegment(segment *s);

		/*! Нахождение конца стрелки по ее продолжению
			\param l Указатель на дугу
			\return Указатель на конечный сегмент
		*/
		end *findParent(link *l);
		
		/*! Функция собирающая туннельную стрелку
			\param e Указатель на конечный сегмент
			\return Путь дуги
		*/
		QPainterPath  drawTunnel(end *e);
        
		/*! Функция отрисовки стрелки.Переопределена для изменения эффекта выделения.
		*/
		void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 );

		/*! Синхронизация полей класса с содержимым текстового поля.
		*/
        inline void TextChanged(void){name = annotation->toPlainText();}
};


#endif
